package net.csdn.business.mirror.service.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.domain.mirror.AddMirrorImportDataDTO;
import net.csdn.business.common.domain.mirror.MyGithubDto;
import net.csdn.business.common.enums.ResultCodeEnum;
import net.csdn.business.common.exception.BusinessException;
import net.csdn.business.common.utils.RedisLockUtils;
import net.csdn.business.common.utils.SurvivalClamProcessor;
import net.csdn.business.mirror.SpringBeanUtil;
import net.csdn.business.mirror.common.Constants;
import net.csdn.business.mirror.config.ImportMirrorConfig;
import net.csdn.business.mirror.entity.bean.*;
import net.csdn.business.mirror.enums.ProjectMirrorEnum;
import net.csdn.business.mirror.enums.ScheduleTaskEnum;
import net.csdn.business.mirror.enums.UserTokenEnum;
import net.csdn.business.mirror.service.*;
import net.csdn.business.mirror.utils.TimeUtil;
import org.apache.commons.lang3.StringUtils;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ImportMirrorServiceImpl implements ImportMirrorService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ImportMirrorConfig importMirrorConfig;
    @Lazy
    @Autowired
    private ProjectMirrorDataService projectMirrorDataService;
    @Autowired
    private ScheduleTaskService scheduleTaskService;
    @Autowired
    private ProjectMirrorBlackService projectMirrorBlackService;
    @Autowired
    private GithubTokenService githubTokenService;
    @Autowired
    private UserGithubTokenService userGithubTokenService;

    @Autowired
    private RedisLockUtils redisLockUtils;

    // 插入队列ID
    @Override
    public void push(Long id) {
        push(id, false);
    }

    @Override
    public void push(Long id, boolean isLeft) {
        push(Arrays.asList(id), isLeft);
    }

    @Override
    public void push(List<Long> ids) {
        push(ids, false);
    }

    @Override
    public void push(List<Long> ids, boolean isLeft) {
        List<String> idList = ids.stream().map(String::valueOf).collect(Collectors.toList());
        if (isLeft) {
            stringRedisTemplate.opsForList().leftPushAll(Constants.MIRROR_IMPORT_KEY, idList);
        } else {
            stringRedisTemplate.opsForList().rightPushAll(Constants.MIRROR_IMPORT_KEY, idList);
        }
    }

    public long pop() {
        String id = stringRedisTemplate.opsForList().leftPop(Constants.MIRROR_IMPORT_KEY);
        if (StringUtils.isEmpty(id)) {
            return 0L;
        }
        // 删除重复的值
        stringRedisTemplate.opsForList().remove(Constants.MIRROR_IMPORT_KEY, Integer.MAX_VALUE, id);
        return Long.parseLong(id);
    }

    //@Override
    public AddMirrorImportDataDTO.Info getMirrorInfo(String fullPath) {
        String key = String.format(Constants.MIRROR_INFO_KEY, fullPath);
        String s = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isEmpty(s)) {
            return null;
        }
        return JSON.parseObject(s, AddMirrorImportDataDTO.Info.class);
    }

    // 执行队列任务
    @Override
    public void exec() {
        Long id=this.pop();
        if(id==0L){
           return;
        }
        //增加分布式锁，保证任务不被并发执行
        StringBuilder key=new StringBuilder("mirror:");
        key.append(id);
        //增加分布式锁，防止多实例时候并发执行，锁是30秒，守护线程在三分之二过期时间之前进行续期
        String identifier=redisLockUtils.lock(key.toString(),30);
        if(identifier!=null){
            log.info("---------project mirror consumer task: id:{} is started-------",id);
            //定义锁的延时守护线程
            SurvivalClamProcessor survivalClamProcessor = new SurvivalClamProcessor(redisLockUtils,key.toString(), 30);;
            //定义守护线程
            Thread survivalThread = new Thread(survivalClamProcessor);
            survivalThread.setDaemon(Boolean.TRUE);
            survivalThread.start();
            try{
                this.exec(id);
            }catch (Exception e){
                e.printStackTrace();
                log.error("---------project mirror consumer task:id:{}  error:{} ----",id,e.getMessage());
            }finally {
                redisLockUtils.unlock(key.toString(),identifier);
            }
            log.info("---------project mirror consumer task: id:{} is finished-------",id);
        }else{
            log.info("---------project mirror consumer task: id:{} is cancled-------",id);
        }
    }

    @Override
    public void exec(long id) {
        if (id < 1) {
            return;
        }
        ProjectMirrorData data = projectMirrorDataService.getById(id);
        if (Objects.isNull(data)) {
            return;
        }
        //查询云效任务执行堆积情况，如果超过最大限制，则重新放到队列排队处理
        // ProjectImportCountVO projectImportCountVO = adapterService.importCount();
        int abroadTotalCount = 0;
        int domesticTotalCount = 0;
        /*if (Objects.nonNull(projectImportCountVO) && !CollectionUtils.isEmpty(projectImportCountVO.getItems())) {
            for (ProjectImportCountVO.Item item : projectImportCountVO.getItems()) {
                boolean abroad = false;
                for (String u : importMirrorConfig.getAbroadUrls()) {
                    if (!abroad && item.getHost().contains(u)) {
                        abroad = true;
                        abroadTotalCount += item.getImportingCount();
                        abroadTotalCount += item.getMirroringCount();
                    }
                }
                if (!abroad) {
                    //domesticTotalCount += item.getImportingCount();
                    //domesticTotalCount += item.getMirroringCount();
                }
            }
        }*/
        UserTokenEnum.Type t = UserTokenEnum.Type.parse(data.getType());
        if ((t.isAbroad() && importMirrorConfig.getLimitAbroadCount() <= abroadTotalCount) ||
                (!t.isAbroad() && importMirrorConfig.getLimitDomesticCount() <= domesticTotalCount)) {
            push(id, true);
            log.error("-----import task exec max limit with host: {}, abroadTotalCount: {}, domesticTotalCount: {}-----", t.getBaseUrl(), abroadTotalCount, domesticTotalCount);
            return;
        }
        ProjectMirrorEnum.Status statusEnum = ProjectMirrorEnum.Status.parse(data.getStatus());
        switch (statusEnum) {
            case _INIT:
            case _0:
            case _7:
                importProject(data);
                break;
            case _1:
            case _2:
            case _3:
            case _4:
            case _6:
                mirrorProject(data);
                break;
            default:
                log.info("-----mirror consumer task,data full path:{} status is : {} ,skip -----", data.getFullPath(),statusEnum.name());
                break;
        }
    }

    private void importProject(ProjectMirrorData data) {
        // 用户不校验黑名单 mirror需要
        if (data.getUserType() == ProjectMirrorEnum.UserType._0.code) {
            ProjectMirrorBlack black = projectMirrorBlackService.getByRepoPath(data.getRepoPath());
            if (Objects.nonNull(black)) {
                projectMirrorDataService.updateAllStatus(data, ProjectMirrorEnum.Status._5.code);
                log.error("-----mirror import  project in black skipped, repoPath: {}, fullPath: {}-----", data.getRepoPath(), data.getFullPath());
                return;
            }
        }
        //ImportProjectDto dto = new ImportProjectDto();
        String name = data.getFullPath().split("/")[1];
        UserTokenEnum.Type t = UserTokenEnum.Type.parse(data.getType());
        //dto.setName(name);
        //dto.setPath(name);
        Integer userType = data.getUserType();
        if(userType.equals(ProjectMirrorEnum.UserType._0.code)){
            // dto.setVisibility_level(ProjectEnum.VisibilityLevelEnum.PUBLIC.getCode());
        }else{
            //dto.setVisibility_level(data.getVisibilityLevel());
        }
        //dto.setDescription(data.getDescription());
        //dto.setImport_url(t.getBaseUrl() + data.getRepoPath() + ".git");
        //dto.setSync(false);
        //dto.setCreate_parent_path(false);
        //dto.setExtern_uid(data.getCreatorId());
        //dto.setOrganization_id(data.getTargetOrgId());
        //dto.setTarget_org_id(data.getTargetOrgId());
        String creatorId=data.getCreatorId();
        Integer type=data.getType(); //平台类型：0： github 1 ：gitee
        //查询用户在镜像平台的账号和有效token
        UserGithubToken userToken = userGithubTokenService.getToken(creatorId,type);
        if(userToken==null||userToken.getStatus().equals(0)){
            projectMirrorDataService.updateMirrorData(data.getId(), ProjectMirrorEnum.Status._7.code, null,new Date(), "token失效，请更换");
            return;
        }else{
            //dto.setImport_account(userToken.getThirdUsername());
            //dto.setImport_token(userToken.getData());
        }
        log.info("-----importProject start: repoPath: {}, fullPath: {}------", data.getRepoPath(), data.getFullPath());
        try {
            projectMirrorDataService.updateMirrorData(data.getId(), ProjectMirrorEnum.Status._0.code, null, new Date(),null);
            //adapterService.importProject(dto);
        }catch (BusinessException e){
            String failMsg=e.getMsg();
            log.error("-----import Project error: repoPath: {}, fullPath: {} ,message:{}", data.getRepoPath(), data.getFullPath(),failMsg);
            //项目导入状态更新为失败
            projectMirrorDataService.updateMirrorData(data.getId(), ProjectMirrorEnum.Status._7.code, null,new Date(), failMsg);
        }
    }

    private void mirrorProject(ProjectMirrorData data) {
        if (Objects.isNull(data.getProjectId())) {
            log.error("mirrorProject skipped, id: {} 未导入", data.getId());
            return;
        }
        Date now = new Date();
       /* if (data.getNextExecutionAt().after(now)) {
            log.error("mirrorProject skipped, id: {} 还未到同步时间", data.getId());
            return;
        }*/
        // 用户不校验黑名单 mirror需要
        if (data.getUserType() == ProjectMirrorEnum.UserType._0.code) {
            ProjectMirrorBlack black = projectMirrorBlackService.getByRepoPath(data.getRepoPath());
            if (Objects.nonNull(black)) {
                projectMirrorDataService.updateAllStatus(data, ProjectMirrorEnum.Status._5.code);
                log.error("mirror import skipped, repoPath: {}, fullPath: {} 已加入黑名单", data.getRepoPath(), data.getFullPath());
                return;
            }
        }
        long minutes = 1440L; // 默认24h
        ScheduleTask scheduleTask = scheduleTaskService.getLastByName(ScheduleTaskEnum.Name.MIRROR.name());
        if (Objects.nonNull(scheduleTask)) {
            Duration duration = TimeUtil.getCronDuration(scheduleTask.getCron());
            minutes = duration.toMinutes();
        }
        Date nextExecutionAt = TimeUtil.afterMins(data.getNextSyncExecutionAt(), minutes);
        if (nextExecutionAt.before(now)) {
            nextExecutionAt = now;
        }
        //MirrorProjectDto dto = new MirrorProjectDto();
        //dto.setRepositoryId(data.getProjectId());
        //dto.setExtern_uid(data.getCreatorId());
        //dto.setOrganization_id(data.getTargetOrgId());
        String creatorId=data.getCreatorId();
        Integer type=data.getType(); //平台类型：0： github 1 ：gitee
        //查询用户在镜像平台的账号和有效token
        UserGithubToken userToken = userGithubTokenService.getToken(creatorId,type);
        if(userToken==null||userToken.getStatus().equals(0)){
            projectMirrorDataService.updateMirrorData(data.getId(), ProjectMirrorEnum.Status._4.code, null,now, "token失效，请更换");
            return;
        }else{
            //dto.setAccount(userToken.getThirdUsername());
            //dto.setToken(userToken.getData());
        }
        try {
            log.info("-----mirrorProject start: repoPath: {}, fullPath: {}------", data.getRepoPath(), data.getFullPath());
            projectMirrorDataService.updateMirrorData(data.getId(), ProjectMirrorEnum.Status._2.code, nextExecutionAt, now,null);
            //adapterService.mirror(dto);
        } catch (BusinessException e) {
            String failMsg=e.getMsg();
            log.error("-----mirror Project error: repoPath: {}, fullPath: {} ,message:{}-----", data.getRepoPath(), data.getFullPath(),failMsg);
            //项目导入状态更新为失败
            projectMirrorDataService.updateMirrorData(data.getId(),  ProjectMirrorEnum.Status._4.code, null,null, failMsg);
        }
    }

    private String getDescription(ProjectMirrorData data) throws BusinessException {
        if (data.getType() == UserTokenEnum.Type.GITHUB.getCode()) { // github mirror
            GitHub gitHub = null;
            if (data.getUserType() == ProjectMirrorEnum.UserType._0.code) {
                List<GithubToken> tokens = githubTokenService.getAllToken();
                gitHub = githubTokenService.getGithubClient(tokens);
            } else { // github user
                if (StringUtils.isEmpty(data.getToken())) {
                    return null;
                }
                try {
                    gitHub = new GitHubBuilder().withOAuthToken(data.getToken()).build();
                } catch (Exception e) {
                    log.error("get user github client error token: {}", data.getToken(), e);
                    throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_REPOSITORY_ERROR);
                }
            }
            if (Objects.isNull(gitHub)) {
                log.error("github client all invalid");
                throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_REPOSITORY_ERROR);
            }
            try {
                GHRepository ghRepository = gitHub.getRepository(data.getRepoPath());
                if (Objects.isNull(ghRepository)) {
                    return null;
                }
                return ghRepository.getDescription();
            } catch (Exception e) {
                return null;
            }
        } else if (data.getType() == UserTokenEnum.Type.GITEE.getCode()) { // gitee user
            UserTokenEnum.Type t = UserTokenEnum.Type.GITEE;
            MyGithubDto.Repository repository = SpringBeanUtil.getBean(t.getThirdApiService()).userRepo(data.getToken(), data.getRepoPath());
            if (Objects.isNull(repository)) {
                return null;
            }
            return repository.getDescription();
        }
        return null;
    }
}